
const TriggerType = Object.freeze({
  SET: 'SET',
  ADD: 'ADD',
  DELETE: 'DELETE'
})

const ITERATE_KEY = Symbol()
const MAP_KEY_ITERATE_KEY = Symbol()

let activeEffect

const effectStack = []

function effect (fn, options = {}) {

  const effectFn = () => {
    cleanup(effectFn)
    activeEffect = effectFn
    effectStack.push(effectFn)
    const res = fn()
    effectStack.pop()

    activeEffect = effectStack[effectStack.length - 1]

    return res
  }


  effectFn.options = options
  effectFn.deps = []

  if (!effectFn.options.lazy) {
    effectFn()
  }

  return effectFn
}

function cleanup (effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }

  effectFn.deps.length = 0
}

const bucket = new WeakMap()

const track = (target, key) => {

  // 禁止追踪则直接返回
  if (!activeEffect || !shouldTrack) return;

  if (!activeEffect) {
    return target[key]
  }

  // 1. 获取当前对象对应的依赖集合
  let depsMap = bucket.get(target)

  if (!depsMap) {
    depsMap = new Map()
    bucket.set(target, depsMap)
  }

  // 2. 获取当前属性对应的依赖集合
  let deps = depsMap.get(key)

  if (!deps) {
    deps = new Set()
    depsMap.set(key, deps)
  }

  deps.add(activeEffect)

  activeEffect.deps.push(deps)

}

const trigger = (target, key, type, newValue) => {
  // 1. 获取当前对象对应的依赖集合
  const depsMap = bucket.get(target)
  if (!depsMap) return

  // 2. 获取当前属性对应的依赖集合
  const effects = depsMap.get(key)

  // 3. 执行依赖函数
  const effectsToRun = new Set()
  effects && effects.forEach((fn) => {
    if (activeEffect !== fn) { // 避免自己执行自己 无限递归爆栈
      effectsToRun.add(fn)
    }
  })

  // 类型是 添加或删除 且target 是map 的时候 
  if (
    (
      type === TriggerType.ADD ||
      type === TriggerType.DELETE
    ) && (Object.prototype.toString.call(target) === '[object Map]')
  ) {
    const iterateEffects = depsMap.get(MAP_KEY_ITERATE_KEY)
    iterateEffects && iterateEffects.forEach((effectFn) => {
      if (activeEffect !== effectFn) { // 避免自己执行自己 无限递归爆栈
        effectsToRun.add(effectFn)
      }
    })

  }


  if (
    type === TriggerType.ADD ||
    type === TriggerType.DELETE ||
    (type === TriggerType.SET && Object.prototype.toString.call(target) === '[object Map]')
  ) { // 如果是添加 或 删除
    const iterateEffects = depsMap.get(ITERATE_KEY)
    iterateEffects && iterateEffects.forEach((effectFn) => {
      if (activeEffect !== effectFn) { // 避免自己执行自己 无限递归爆栈
        effectsToRun.add(effectFn)
      }
    })
  }

  // 当操作类型是 添加  且代理对象是数组的时候 取出并执行和length属性相关联的副作用函数
  if (type === TriggerType.ADD && Array.isArray(target)) {
    const lengthEffects = depsMap.get('length')
    lengthEffects && lengthEffects.forEach((effectFn) => {
      if (activeEffect !== effectFn) { // 避免自己执行自己 无限递归爆栈
        effectsToRun.add(effectFn)
      }
    })
  }


  // 数组 且 操作length
  if (Array.isArray(target) && key === 'length') {

    depsMap.forEach((effects, key) => {
      // 当索引大于或等于 新的length值 则取出并执行和索引相关的副作用函数
      // 疑惑点key: 当访问length时就是length 当通过索引访问元素的时候就是索引值
      if (key >= newValue) {

        effects.forEach((effectFn) => {
          if (activeEffect !== effectFn) { // 避免自己执行自己 无限递归爆栈
            effectsToRun.add(effectFn)
          }
        })
      }
    })
  }


  effectsToRun && effectsToRun.forEach((effectFn) => {
    // 如果存在调度器 则调用调度器 把函数作为参数传入
    if (effectFn.options.scheduler) {
      effectFn.options.scheduler(effectFn)
    } else {
      effectFn()
    }
  })
}


// 重寫 數組方法
const arrayInstrumentations = {};

['includes', 'indexOf', 'lastIndexOf'].forEach(method => {
  const originMethod = Array.prototype[method]
  arrayInstrumentations[method] = function (...args) {
    // this是代理对象，先在代理对象中查找
    let res = originMethod.apply(this, args)

    //如果返回false 则表示 找不到， 通过this.raw 拿到原数组  再去其中查找并更新res值
    if (res === false) {
      res = originMethod.apply(this.raw, args)
    }

    return res
  }
})

// 标记变量 代表是否进行追踪 默认值为true 允许追踪
let shouldTrack = true;

['push', 'pop', 'shift', 'unshift', 'splice'].forEach(method => {
  // 获取原始的数组方法
  const originMethod = Array.prototype[method]
  arrayInstrumentations[method] = function (...args) {
    // 调用原始方法的时候禁止追踪
    shouldTrack = false
    let res = originMethod.apply(this, args)

    // 调用原始方法后 就恢复原来行为 允许追踪
    shouldTrack = true

    return res
  }

})


// 迭代器方法
function iterationMethod () {
  const target = this.raw
  // 获取原始迭代器方法
  const itr = target[Symbol.iterator]()
  const wrap = (val) => typeof val === 'object' ? reactive(val) : val

  // 调用track函数建立响应式联系
  track(target, ITERATE_KEY)

  // 返回自定义 迭代器
  return {
    // 迭代器协议
    next () {
      const { value, done } = itr.next()
      return {
        value: value ? [wrap(value[0]), wrap(value[1])] : value,
        done
      }
    },

    // 可迭代协议
    [Symbol.iterator] () {
      return this
    }
  }
}

function keysOrValuesIterationMethod (isKeys = false) {
  const target = this.raw
  console.log('%c [ target ]-236', 'font-size:13px; background:pink; color:#bf2c9f;', target)
  const itr = target[isKeys ? 'values' : 'keys']()
  const wrap = (val) => typeof val === 'object' ? reactive(val) : val

  // 调用track函数建立响应式联系
  track(target, isKeys ? MAP_KEY_ITERATE_KEY : ITERATE_KEY)

  // 返回自定义 迭代器
  return {
    // 迭代器协议
    next () {
      const { value, done } = itr.next()
      return {
        value: wrap(value),
        done
      }
    },

    // 可迭代协议
    [Symbol.iterator] () {
      return this
    }
  }
}


const mutableInstrumentations = {
  add (key) {
    // this 只向 的是代理对象
    const target = this.raw
    const hasKey = target.has(key)
    // 通过原始数据对象执行add方法 
    const res = target.add(key)

    // 如果没有key 则触发响应式
    if (!hasKey) {
      // 调用trigger 函数触发响应  并指定操作类型为ADD
      trigger(target, key, TriggerType.ADD)
    }

    return res
  },

  delete (key) {
    const target = this.raw
    const hasKey = target.has(key)
    const res = target.delete(key)

    if (hasKey) {
      trigger(target, key, TriggerType.DELETE)
    }

    return res
  },

  get (key) {
    const target = this.raw
    const hasKey = target.has(key)
    // 追踪依赖,触发响应式联系
    track(target, key)

    // 返回响应式数据
    if (hasKey) {
      const res = target.get(key)
      return typeof res === "object" ? reactive(res) : res
    }
  },

  set (key, value) {
    const target = this.raw
    const had = target.has(key)
    // 获取旧值 
    const oldValue = target.get(key)

    // target.set(key,value)
    // 获取原始数据 防止数据污染
    const rawValue = value.raw || value
    target.set(key, rawValue)

    if (!had) {
      trigger(target, key, TriggerType.ADD)
    } else if (oldValue !== value || (oldValue === oldValue && value === value)) {
      // 如果不存在 且 值变了就是set 意味着修改
      trigger(target, key, TriggerType.SET)
    }
  },

  // 允许接受第二个参数
  forEach (callback, thisArg) {

    //当参数为非原始数据时,无响应式
    // const target = this.raw
    // track(target, ITERATE_KEY)
    // target.forEach(callback)

    // 修正
    const wrap = (val) => typeof val === 'object' ? reactive(val) : val
    const target = this.raw
    track(target, ITERATE_KEY)

    target.forEach((value, key) => {
      // 手动调用callback 将参数进行响应式处理 并将结果返回
      callback.call(thisArg, wrap(value), wrap(key), this)
    })
  },

  [Symbol.iterator]: iterationMethod,
  entries: iterationMethod,

  values: () => keysOrValuesIterationMethod(false),
  keys: () => keysOrValuesIterationMethod(true),

}


function createReactive (obj, isShallow = false, isReadonly = false) {
  return new Proxy(obj, {
    get (target, key, receiver) {

      // 代理对象可以通过 raw 属性访问原始数据
      if (key === 'raw') {
        return target
      }

      const value = target[key]
      let res

      if(value.__v_isRef){
        res = value.value
      }else{
        res = Reflect.get(target, key, receiver)
      }

      // 只读则不需要追踪响应式
      if (!isReadonly) {
        track(target, key)
      }

      // 浅响应 直接返回对象
      if (isShallow) {
        return res
      }

      // 如果是对象则继续递归 返回响应式数据
      if (typeof res === 'object' && res !== null) {
        return isReadonly ? readonly(res) : reactive(res)
      }

      return res
    },

    set (target, key, newValue, receiver) {

      if (isReadonly) {
        console.warn(`${key}属性是只读的`)
        return true
      }

      // 获取旧值
      const oldValue = target[key].__v_isRef? target[key].value : target[key]

      const type = Object.prototype.hasOwnProperty.call(target, key) ?
        TriggerType.SET : TriggerType.ADD //

      const res = Reflect.set(target, key, newValue, receiver)


      // 代理对象可以通过 raw 属性访问原始数据
      if (target === receiver.raw) {
        // 考虑新旧值变化问题，且NAN 问题
        // NaN !== NaN  true
        // NaN === NaN  false
        if (newValue !== oldValue && (oldValue === oldValue || newValue === newValue)) {
          trigger(target, key, type)
        }
      }

      return res
    },

    deleteProperty (target, key) {

      if (isReadonly) {
        console.warn(`${key}属性是只读的`)
        return true
      }

      const hadKey = Object.prototype.hasOwnProperty.call(target, key)
      const res = Reflect.deleteProperty(target, key)
      if (res && hadKey) {
        trigger(target, key, TriggerType.DELETE)
      }
      return res
    },

    has (target, key) {
      track(target, key)
      return Reflect.has(target, key)
    },

    ownKeys (target) {
      track(target, ITERATE_KEY)
      return Reflect.ownKeys(target)
    }

  })
}

// 定义 一个map 实例， 存储原始对象到代理对象的映射
const reactiveMap = new Map()

function reactive (obj) {

  // 如果这个对象已经被代理过了，就直接返回代理对象
  const existProxy = reactiveMap.get(obj)
  if (existProxy) return existProxy

  // 否则，创建新的代理对象
  const proxy = createReactive(obj)
  // 存储到map中， 避免重复创建
  reactiveMap.set(obj, proxy)

  return proxy
}

function shallowReactive (obj) {
  return createReactive(obj, true)
}

function readonly (obj) {
  return createReactive(obj, false, true)
}

function shallowReadonly (obj) {
  return createReactive(obj, true, true)
}

function ref (val) {
  const wrapper = {
    value: val
  }

  // 使用 object.defineProperty 定义一个不可枚举的属性 __v_isRef ，值为 true
  Object.defineProperty(wrapper, '__v_isRef', {
    value: true
  })

  return reactive(wrapper)
}

function toRef (obj, key) {
  const wrapper = {
    get value () {
      return obj[key]
    },

    set value (val) {
      obj[key] = val
    }
  }

  Object.defineProperty(wrapper, '__v_isRef', {
    value: true
  })

  return wrapper
}

function toRefs (obj) {
  const ret = {}
  for (const key in obj) {
    ret[key] = toRef(obj, key)
  }

  return ret
}

function ProxyRef (target) {
  return new Proxy(target, {
    get (target, key, receiver) {
      const value = Reflect.get(target, key, receiver)

      // 自动脱ref实现
      // 如果value是ref类型 则返回value.value 否则返回value
      return value.__v_isRef ? value.value : value
    },

    set(target, key, newValue, receiver) {
      const value = target[key]

      // 如果value是ref类型 则设置value.value 否则设置valu
      if(value.__v_isRef){
        value.value = newValue
        return true
      }

      return Reflect.set(target, key, newValue, receiver)
    }
  })
}


function clone(obj){
  return JSON.parse(JSON.stringify(obj))
}


// 测试代码
const obj = reactive({
  foo: 1,
  bar: 2,
})

const newObj = ProxyRef({
  ...toRefs(obj)
})

effect(() => {

  console.log('%c [  ]-510', 'font-size:13px; background:pink; color:#bf2c9f;', newObj)
  console.log('%c [  ]-510', 'font-size:13px; background:pink; color:#bf2c9f;', newObj.foo)
})

obj.foo = 100

newObj.foo = 1000

const count = ref(0)
const obj1= reactive({count})

effect(()=>{

  console.log('%c [  ]-555', 'font-size:13px; background:pink; color:#bf2c9f;', obj1)
  console.log('%c [  ]-549', 'font-size:13px; background:pink; color:#bf2c9f;', obj1.count)
})


obj1.count = 1000
obj1.count = 10






